using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.IO;
using System.Windows.Forms;

namespace OpenComposite.EII.CodeGenerators
{
	internal static class CSharpGenerator
	{
		/// <summary>
		/// Generates the code string.
		/// </summary>
		/// <param name="method">The method.</param>
		/// <returns>The C# code string.</returns>
		internal static string GenerateCodeString(CodeMemberMethod method)
		{
			string code = "";
			try {
				CSharpCodeProvider csharp = new CSharpCodeProvider();
				CodeGeneratorOptions options = new CodeGeneratorOptions();
				options.ElseOnClosing = true;
				using ( StringWriter sw = new StringWriter() ) {
					csharp.GenerateCodeFromMember(method, sw, options);
					code = sw.ToString();
				}
			} catch ( Exception ex ) {
				string msg = string.Format("Error while generating C#-code.{0}{1}",
					Environment.NewLine, ex.Message);
				System.Windows.Forms.MessageBox.Show(msg, "Generate C#",
					System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);

				code = "";
			}
			return code;
		}
		/// <summary>
		/// Generates the code string.
		/// </summary>
		/// <param name="codeNamespace">The code namespace.</param>
		/// <returns>The C# code string.</returns>
		internal static string GenerateCodeString(CodeNamespace codeNamespace)
		{
			string code = "";
			try {
				CSharpCodeProvider csharp = new CSharpCodeProvider();
				CodeGeneratorOptions options = new CodeGeneratorOptions();
				options.ElseOnClosing = true;
				options.BracingStyle = "C";
				using ( StringWriter sw = new StringWriter() ) {
					csharp.GenerateCodeFromNamespace(codeNamespace, sw, options);
					code = sw.ToString();
				}
			} catch ( Exception ex ) {
				string msg = string.Format("Error while generating C#-code.{0}{1}",
				    Environment.NewLine, ex.Message);
				//string msg = "Policy is saved, but contains some invalid rules.";
				//System.Windows.Forms.MessageBox.Show(msg, "Generate C#",
				//    System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
				System.Diagnostics.Debug.WriteLine(msg);

				code = "";
			}
			return code;
		}
		/// <summary>
		/// Generates the code string.
		/// </summary>
		/// <param name="codeCompileUnit">The code compile unit.</param>
		/// <returns>The C# code string.</returns>
		internal static string GenerateCodeString(CodeCompileUnit codeCompileUnit)
		{
			if ( codeCompileUnit == null ) return "";

			string code = "";
			CSharpCodeProvider csharp = new CSharpCodeProvider();
			CodeGeneratorOptions options = new CodeGeneratorOptions();
			options.ElseOnClosing = true;
			options.BracingStyle = "C";
			options.IndentString = "\t";
			using ( StringWriter sw = new StringWriter() ) {
				csharp.GenerateCodeFromCompileUnit(codeCompileUnit, sw, options);
				code = sw.ToString();
			}
			return code;
		}

		/// <summary>
		/// Compiles a code string.
		/// </summary>
		/// <param name="code">The code.</param>
		/// <param name="referencedAssemblies">The referenced assemblies.</param>
		/// <returns>The compiler results.</returns>
		internal static CompilerResults CompileCodeString(string code, params string[] referencedAssemblies)
		{
			CSharpCodeProvider csharp = new CSharpCodeProvider();
			CompilerParameters compileparams = new CompilerParameters();
			compileparams.GenerateInMemory = true;
			if ( referencedAssemblies != null ) {
				compileparams.ReferencedAssemblies.AddRange(referencedAssemblies);
			} else {
				compileparams.ReferencedAssemblies.AddRange(
					new string[] {
					"System.dll",
					"System.Data.dll",
					"System.Windows.Forms.dll",
					"System.Xml.dll"
				});
			}
			CompilerResults results = csharp.CompileAssemblyFromSource(compileparams, code);
			if ( results.Errors.HasErrors ) {
				string msg = "";
				int iErrors = 0;
				foreach ( CompilerError error in results.Errors ) {
					msg += "Line " + error.Line.ToString() + ": " + error.ErrorText + Environment.NewLine;
					iErrors++;
				}
				System.Diagnostics.Debug.WriteLine(string.Format("Compile complete -- {0} errors{1}{1}{2}",
																 iErrors, Environment.NewLine, msg));
			}
			return results;
		}

		/// <summary>
		/// Compiles the code to an assembly.
		/// </summary>
		/// <param name="outputFilePath">The output file path.</param>
		/// <param name="compilationUnits">The compilation units.</param>
		/// <param name="referencedAssemblies">The referenced assemblies.</param>
		/// <returns>The <see cref="T:System.CodeDom.Compiler.CompilerResults"/>.</returns>
		internal static CompilerResults CompileCodeToAssembly(
			string outputFilePath, CodeCompileUnit[] compilationUnits, string[] referencedAssemblies)
		{
			CompilerParameters options = new CompilerParameters();
			options.ReferencedAssemblies.AddRange(Deployment.DefaultReferencedAssemblies);
			if ( referencedAssemblies != null && referencedAssemblies.Length > 0 ) {
				foreach ( var refasm in referencedAssemblies ) {
					if ( !options.ReferencedAssemblies.Contains(refasm) ) {
						options.ReferencedAssemblies.Add(refasm);
					}
				}
			}
			options.CompilerOptions = "/optimize+ /debug-";
			options.GenerateExecutable = false;
			if ( string.IsNullOrEmpty(outputFilePath) ) {
				options.GenerateInMemory = true;
			} else {
				string dirPath = Path.GetDirectoryName(outputFilePath);
				if ( !Directory.Exists(dirPath) ) Directory.CreateDirectory(dirPath);
				options.GenerateInMemory = false;
				options.OutputAssembly = outputFilePath;
			}
			options.IncludeDebugInformation = false;

			CSharpCodeProvider cscp = new CSharpCodeProvider();
			return cscp.CompileAssemblyFromDom(options, compilationUnits);
		}

		/// <summary>
		/// Compiles the code to an assembly.
		/// </summary>
		/// <param name="outputFilePath">The output file path.</param>
		/// <param name="sources">The sources.</param>
		/// <param name="referencedAssemblies">The referenced assemblies.</param>
		/// <returns>The <see cref="T:System.CodeDom.Compiler.CompilerResults"/>.</returns>
		internal static CompilerResults CompileCodeToAssembly(
			string outputFilePath, string[] sources, string[] referencedAssemblies)
		{
			CompilerParameters options = new CompilerParameters();
			options.ReferencedAssemblies.AddRange(Deployment.DefaultReferencedAssemblies);
			if ( referencedAssemblies != null && referencedAssemblies.Length > 0 ) {
				foreach ( var refasm in referencedAssemblies ) {
					if ( !options.ReferencedAssemblies.Contains(refasm) ) {
						options.ReferencedAssemblies.Add(refasm);
					}
				}
			}
			options.CompilerOptions = "/optimize+ /debug-";
			options.GenerateExecutable = false;
			if ( string.IsNullOrEmpty(outputFilePath) ) {
				options.GenerateInMemory = true;
			} else {
				string dirPath = Path.GetDirectoryName(outputFilePath);
				if ( !Directory.Exists(dirPath) ) Directory.CreateDirectory(dirPath);
				options.GenerateInMemory = false;
				options.OutputAssembly = outputFilePath;
			}
			options.IncludeDebugInformation = false;

			CSharpCodeProvider cscp = new CSharpCodeProvider();
			return cscp.CompileAssemblyFromSource(options, sources);
		}
	}
}
